// This is fun!

/**
 * So one of these objects will be created EVERY TIME a window is opened. We don't want to USE any but the VERY FIRST
 * that is created. So the ONLY way one should grab a reference to this object is via getInstance.
 *
 * That said, all methods in this class should act in a stateless fashion, whenever possible.
 */

var stateHolder = Components.classes["@mozilla.org/appshell/appShellService;1"].
    getService(Components.interfaces.nsIAppShellService).hiddenDOMWindow;

if ( stateHolder.TestFire )
{
    var x = "I'm here for a debug point.";
}
else
{

    var TestFire = {

    // Each module will correspond with one window.
        modules : new Array(),
        currentModuleIdx : 0,
        currentEngineIdx : 0,
        windowNameHash : {},
        contentHash : {},
        monitoredWindows : {},
        windowCount : 0,
        testset : null,
        selectedTest : null,
        initialized : false,
        instantiated: false,
        util: null,
        md5: null,
        settings : {
            testFireServerUrl: '',
            testFireAgentUrl: 'http://localhost:9292',
            email: '',
            username: '',
            password: ''
        },


        getInstance: function()
        {
            return stateHolder.TestFire;
        },

        init: function( firebug )
        {
            try
            {
                if ( !this.instantiated )
                {
                    this.util = UTIL;
                    this.md5 = MD5;
                    this.createFreshInstance();
                    this.instantiated = true;
                }
                if ( stateHolder.TestFire !== this )
                {
                    alert("Incorrect call to init in TestFire.js");
                }
            }
            catch( ex )
            {this.util.tf_debug(ex); }

        },

        isMonitoringBrowserWindow : function(browserWindow, aTopMostWindow)
        {
            var name = this.getBrowserWindowName(browserWindow);
            if(name != null)
            {
                aTopMostWindow.__testFireWindowName = name;
            }
            return name != null;
        },

        getBrowserWindowName : function(browserWindow)
        {
            for(var name in this.windowNameHash)
            {
                if(this.windowNameHash[name] == browserWindow)
                {
                    return name;
                }
            }
            return null;
        },

        monitorBrowserWindow : function(browserWindow, topMostWindow)
        {
            if(this.removeIsQueued )
            {
                if(this.removeIsQueued == browserWindow)
                {
                    this.removeIsQueued = null;
                }
                else
                {
                    this.removeBrowserWindow(this.removeIsQueued);
                }
            }
            var name = 'engine_' + this.windowCount++;
            this.windowNameHash[name] = browserWindow;
            topMostWindow.__testFireWindowName = name;
        },

        /**
         * Remove a browser window from the map.
         * While a seemingly logical thing to do, this creates bad news when returning to engine_0,
         * the opening window (WINDOW, not browser window), from another page. E.g. engine_0 goes
         * away, and is replaced by some new engine object. Which is bad, as it's wrong.
         * We could fix this with a name test, preventing the root browser window from going away.
         * Instead I'm just going to eat the memory leak(s) for now.
         *
         * @param win The browser window to remove.
         */
        removeBrowserWindow : function(win)
        {

//            if(!this.removeIsQueued)
//            {
//                // this method will be called even when navigating from page to page - we don't need a new
//                // engine on every navigation event, just when a dialog or tab opens.
//                this.removeIsQueued = win;
//                return;
//            }
//            this.removeIsQueued = null;
//            for(var name in this.windowNameHash)
//            {
//                if(this.windowNameHash[name] == win)
//                {
//                    delete this.windowNameHash[name];
//                    return;
//                }
//            }

        },



    /**
     * Either a brand new instance, or we want to create a totally new session (e.g. we commited a Testset and want
     * to create a new one.
     * The current window will become the primary.
     */
        createFreshInstance: function()
        {
            try
            {
                var module = this.modules[this.currentModuleIdx];
                this.modules = new Array();
                if ( module )
                {
                    this.modules.push(module);
                }
                this.currentModuleIdx = 0;
                this.currentEngineIdx = 0;
                this.loadUserSettings();
                this.testset = new TestSet(this);
                this.initialized = false;
                this.setSelectedTest(this.testset.getTests()[0]);

                if(module)
                {
                    module._refresh();
                }
            }
            catch( ex )
            {this.util.tf_debug(ex); }

        },

        openTestSetInstance: function(testset)
        {
            try
            {
                var module = this.modules[this.currentModuleIdx];
                this.modules = new Array();
                if ( module )
                {
                    this.modules.push(module);
                }
                this.currentModuleIdx = 0;
                this.currentEngineIdx = 0;
                this.loadUserSettings();
                this.testset = testset;
                this.initialized = false;
                this.setSelectedTest(this.testset.getTests()[0]);

                if(module)
                {
                    module._refresh();
                }
                this.modules[0].reInitialize();
            }
            catch( ex )
            {this.util.tf_debug(ex); }

        },

    /**
     * Inform the system that a new TestFire module (TestFireExtension object, but I want to rename that) has been
     * created and needs to be initialized.
     */
        initTestFireModule: function( testFireModule )
        {
            try
            {
                this.init();
                this.modules.push(testFireModule);
                testFireModule.setTestFire(this);
                testFireModule._refresh();
            }
            catch( ex )
            {this.util.tf_debug(ex); }

        },

    /**
     * The window is being closed or somesuch. So we might be able to auto-add the Watij window management code via
     * this and the init method. Hard to say. Maybe we don't want to. Whatever.
     */
        destroyTestFireModule: function( testFireModule )
        {
            try
            {
                var ary = new Array();
                for ( var i = 0; i < this.modules.length; i++ )
                {
                    if ( this.modules[i] == testFireModule )
                    {
                        this.modules[i] = null;
                    }
                    else
                    {
                        ary.push(this.modules[i]);
                    }
                }
                this.modules = ary;
            }
            catch( ex )
            {this.util.tf_debug(ex); }

        },

        refreshAll: function()
        {
            for ( var i = 0; i < this.modules.length; i++ )
            {
                this.modules[i]._refresh();
            }
        },













    /*********************************************************************************************************/

        applyToAll: function( _target, _array, _function, arguments, _thisArg )
        {
            for ( var i = 0; i < _array.length; i++ )
            {
                _thisArg = _thisArg ? _thisArg : _array[i];
                if ( _target )
                {
                    _array[i][_function].apply(_thisArg, arguments);
                }
                else
                {
                    _function.apply(_thisArg, arguments);
                }

            }

        },











    /*********************************************************************************************************/

        getTestset: function()
        {
            return this.testset;
        },

        getSelectedTest: function()
        {
            return this.selectedTest;
        },

        setSelectedTest: function( test )
        {
            try
            {
                this.selectedTest = test;
                for ( var i = 0; i < this.modules.length; i++ )
                {
                    var button = this.modules[i].getFBL().$("testFireTargetButton");
                    button.label = "Target (" + test.displayValue + ")";
                }
            }
            catch( ex )
            {this.util.tf_debug(ex); }

        },

        clearTestFireSession: function( caller )
        {

            var okAction = function(){
                this.createFreshInstance();
                this.modules[0].reInitialize();
                return true;
            };
            var cancelAction = function(){return true;};
            var args = {
                caller: this,
                msg: "Are you certain?",
                dialogType: 'Confirm',
                okAction: okAction,
                cancelAction: cancelAction
            };
            window.openDialog("chrome://TestFireExtension/content/dialogs/PromptDialog.xul",
                    "prompt", "chrome, dialog, modal, resizable=yes,centerscreen",
                    args);

        },




        updateUserSettings: function(){
        },




    /**
     * Write the user settings out into the settings file.
     * @todo Implement server-side storage of user prefs.
     */
        saveUserSettings: function( doc )
        {
            try
            {
                var str = "{\n";
                for ( var name in this.settings )
                {
                    this.settings[name] = doc.getElementById(name).value;
                    str += name + ": '" + this.settings[name] + "',\n";
                }
                str += "}";
                this.util.writeFile(str, "test_fire_settings.json", false, true);
                this.updateUserSettings();
            }
            catch( ex )
            {
                this.util.tf_debug(ex);
            }
        },

        loadUserFile: function()
        {
            var okAction = function(){
                var func = this.util.bindFixed(this.openTestSetFile, this);
                window.setTimeout(func, 100);
                return true;
            };
            var cancelAction = function(){return true;};
            var args = {
                caller: this,
                msg: "This will close the current set: continue anyway?",
                dialogType: 'Confirm',
                okAction: okAction,
                cancelAction: cancelAction
            };
            window.openDialog("chrome://TestFireExtension/content/dialogs/PromptDialog.xul",
                    "prompt", "chrome, dialog, modal, resizable=yes,centerscreen",
                    args);

        },

        openTestSetFile: function()
        {
            try
            {
                var nsIFilePicker = Components.interfaces.nsIFilePicker;
                var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
                fp.init(window, "Select a File", nsIFilePicker.modeOpen);
                var res = fp.show();
                if ( res == nsIFilePicker.returnOK )
                {
                    try
                    {

                        var thefile = fp.file;

                        var obj_InputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
                        obj_InputStream.init(thefile, 0x01, 0444, null);

                        var obj_ScriptableIO = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
                        obj_ScriptableIO.init(obj_InputStream);

                        var str = obj_ScriptableIO.read(thefile.fileSize);

                        obj_ScriptableIO.close();
                        obj_InputStream.close();

                        var domParser = new DOMParser();
                        var dom = domParser.parseFromString(str, "text/xml");
                    // print the name of the root element or error message

                        var el = dom.getElementsByTagName("TestSet").item(0);
                        var testSet = new TestSet(this);
                        testSet.open(el);
                        testSet.testFire.openTestSetInstance(testSet);
                        testSet.currentFilePath = fp.file.path;

                    }
                    catch( ex )
                    {
                        Firebug.TestFire.util.tf_debug(ex);
                    }
                }
            }
            catch( ex )
            {
                Firebug.TestFire.util.tf_debug(ex);
            }
        },

        saveUserFile: function(context, prompt )
        {
            var str = FirebugContext.getPanel("TestFireCodePanel").asString;
            try
            {
                var path = this.testset.currentFilePath;
                if ( prompt || !path )
                {
                    var nsIFilePicker = Components.interfaces.nsIFilePicker;
                    var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
                    fp.init(window, "Save as: ", nsIFilePicker.modeSave);
                    fp.appendFilters(nsIFilePicker.filterXML);
                    fp.defaultString = this.testset.name + ".xml";
                    var res = fp.show();
                    if ( res == nsIFilePicker.returnOK )
                    {
                        try
                        {
                            path = fp.file.path;
                        }
                        catch( ex )
                        {
                            Firebug.TestFire.util.tf_debug(ex);
                        }
                    }
                }
                if(!path || path.length == 0)
                {
                    alert("Could not write to file. No valid path returned.");
                }
                else
                {
                    var idx = path.lastIndexOf('.');
                    if(idx < 0 || path.substring(idx) != '.xml')
                    {
                        path = path + ".xml";
                    }
                    this.testset.currentFilePath = path;
                    try
                    {
                        this.util.writeFile(str, path, false, false);
                    }
                    catch(e)
                    {
                        this.testset.currentFilePath = null;
                        alert("Could not write to file:\n     '" + path + "'");
                    }
                }

            }
            catch( ex )
            {
                this.util.tf_debug(ex);
            }

        },

        modifyUserSettings: function(){

            var okAction = function(responseArgs)
            {
                this.saveUserSettings(responseArgs.dialogDoc);
                return true;
            };
            var args = {
                caller: this,
                settings: this.settings,
                okAction: okAction
            };
            window.openDialog("chrome://TestFireExtension/content/dialogs/NetworkSettingsDialog.xul",
                "prompt", "chrome, dialog, modal, resizable=yes,centerscreen", args);

        },


    /**
     * Read the user settings in from the settings file.
     */
        loadUserSettings: function()
        {
            try
            {
                var text = this.util.readFile("test_fire_settings.json", true);
            }
            catch(ex)
            {
                alert(ex);
                // need to set settings, @todo check that we have the correct (file doesn't exist) error.
                this.modifyUserSettings();
            }
            try{
                var loadedSettings = eval('(' + text + ')');
                for ( var name in loadedSettings )
                {
                    this.settings[name] = loadedSettings[name];
                }            }
            catch( ex )
            {
                alert("Could not load server settings from user profile. \n"
                    + "You may not be able to submit tests directly to the server.");                
            }

        }



    };
    stateHolder.TestFire = TestFire;
}